﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Diagnostics;

namespace LowLevelGraphics.ColorSpaces
{
    /// <summary>
    /// YCbCr Channel
    /// </summary>
    public enum YCbCrChannel
    {
        /// <summary>
        /// 
        /// </summary>
        Y = 0,
        /// <summary>
        /// 
        /// </summary>
        Cb = 1,
        /// <summary>
        /// 
        /// </summary>
        Cr = 2
    }

    /// <summary>
    /// Class to convert RGB to YCbCr and back
    /// </summary>
    [DebuggerDisplay("Y: {m_Y}, Cb: {m_Cb}, Cr: {m_Cr}")]
    public class YCbCr : AbstractColorSpace
    {
        protected double m_Y = -1.0f;
        protected double m_Cb = -1.0f;
        protected double m_Cr = -1.0f;

        /// <summary>
        /// constructor
        /// </summary>
        /// <param name="_color"></param>
        public YCbCr(Color _color)
            : base(_color)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="YCbCr"/> class.
        /// </summary>
        /// <param name="_Y">The _ Y.</param>
        /// <param name="_Cb">The _ cb.</param>
        /// <param name="_Cr">The _ cr.</param>
        public YCbCr(double _Y, double _Cb, double _Cr)
        {
            m_Y = _Y;
            m_Cb = _Cb;
            m_Cr = _Cr;
        }

        /// <summary>
        /// used by clone to create a deep copy
        /// </summary>
        /// <param name="_ycbcr"></param>
        internal YCbCr(YCbCr _ycbcr)
        {
            this.m_Color = _ycbcr.m_Color;
            this.m_Y = _ycbcr.m_Y;
            this.m_Cb = _ycbcr.m_Cb;
            this.m_Cr = _ycbcr.m_Cr;
        }

        /// <summary>
        /// Enables the caller to cast
        /// </summary>
        /// <param name="_yCbCr"></param>
        /// <returns></returns>
        public static explicit operator YCbCr(Color _yCbCr)
        {
            return new YCbCr(_yCbCr);
        }

        /// <summary>
        /// gets or sets Y
        /// </summary>
        public double Y
        {
            get { return m_Y;}
            set { m_Y = value; }
        }

        /// <summary>
        /// Gets or sets Cb
        /// </summary>
        public double Cb
        {
            get { return m_Cb; }
            set { m_Cb = value; }
        }

        /// <summary>
        /// Gets or sets Cr
        /// </summary>
        public double Cr
        {
            get { return m_Cr; }
            set { m_Cr = value; }
        }

        /// <summary>
        /// Converts color into this color space
        /// This formula should be correct because I saw several sites stating exactly the same one
        /// </summary>
        protected override void CalcValues()
        {
            m_Y = 0.299f * m_Color.R + 0.587f * m_Color.G + 0.114f * m_Color.B;
            m_Cb = -0.169f * m_Color.R - 0.332f * m_Color.G + 0.500f * m_Color.B; //b-Y
            m_Cr = 0.500f * m_Color.R - 0.419f * m_Color.G - 0.081f * m_Color.B;  //r-Y
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override AbstractColorSpace Normalize()
        {
            YCbCr yCbCr = new YCbCr(m_Color);
            yCbCr.m_Cb = this.m_Cb;
            yCbCr.m_Cr = this.m_Cr;
            yCbCr.m_Y = this.m_Y;
            yCbCr.Color = this.m_Color;
            return yCbCr;
        }

        /// <summary>
        /// retransform color back to rgb color space
        /// </summary>
        /// <returns></returns>
        public override Color ToColor()
        {
            double fR = (298 * m_Y +409 * m_Cr - 57065)/256;
            double fG = (298 * m_Y - 100 * m_Cb - 208 * m_Cr + 34658) / 256;
            double fB = (298 * m_Y +516 * m_Cb - 70894)/256;

            if (fR < 0) fR = 0; if (fR > 255) fR = 255;
            if (fG < 0) fG = 0; if (fG > 255) fG = 255;
            if (fB < 0) fB = 0; if (fB > 255) fB = 255;

            return Color.FromArgb((int)fR, (int)fG, (int)fB);
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return string.Format("Color Y = {0}, Cb = {1}, Cr = {2}",m_Y, m_Cb, m_Cr);
        }

        /// <summary>
        /// Gets the values.
        /// </summary>
        /// <returns></returns>
        public override object[] GetValues()
        {
            return new object[] { m_Y, m_Cb, m_Cr };
        }

        /// <summary>
        /// Sets the values.
        /// </summary>
        /// <param name="_aObject">The _a object.</param>
        public override void SetValues(object[] _aObject)
        {
            m_Y = (double)_aObject[0];
            m_Cb = (double)_aObject[1];
            m_Cr = (double)_aObject[2];
        }

        /// <summary>
        /// Creates a deep copy
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new YCbCr(this);
        }

    }
}
